<?php
/**
 * yumao
 * 2018-7-26
 * 监控报警相关控制器
 */
namespace app\controllers;
use yii;
use app\models\MonitorReport;
use app\models\MonitorReportMessage;
use app\components\redis\RedisMarketingEmail;
use app\components\redis\RedisLock;
use Ares333\Curl\Toolkit;
use app\components\msgcenter\MsgCenter;
use app\models\Elog;
use app\components\MonitorReportComponent;
class MonitorReportController extends \app\base\Controller
{
    private $configInfo;
    
    /**
     * 初始化相关配置信息
     */
    public function init()
    {
        // 系统名称相关
        $this->configInfo['system_id'][1] = '系统邮件';
        $this->configInfo['system_id'][2] = '营销邮件';
        $this->configInfo['system_id'][3] = '系统管理';
        
        // 监控类型相关
        $this->configInfo['monitor_mode'][0] = '周期性监控';
        $this->configInfo['monitor_mode'][1] = '一次性监控';
        $this->configInfo['monitor_mode'][2] = '隔指定分钟监控';
        
        // 监控条件类型相关
        $this->configInfo['monitor_condition_type'][0] = 'sql';
        $this->configInfo['monitor_condition_type'][1] = 'url';
        
        // 告警触发条件
        $this->configInfo['report_type'][0] = '等于';
        $this->configInfo['report_type'][1] = '大于';
        $this->configInfo['report_type'][2] = '小于';
        
        // 告警通知方式
        $this->configInfo['report_channel'][1] = 'rtx';
        $this->configInfo['report_channel'][2] = '微信';
        $this->configInfo['report_channel'][3] = '邮件';
        $this->configInfo['report_channel'][4] = '短信';
        $this->configInfo['report_channel'][5] = '电话';
        
        // 是否需要自动恢复
        $this->configInfo['is_need_retry'][0] = "否";
        $this->configInfo['is_need_retry'][1] = "是";
        
        // 配置状态
        $this->configInfo['status'][0] = '未启用';
        $this->configInfo['status'][1] = '启用';
        
        // 网站配置信息 （由于联系人列表暂时没有移出来共用营销邮件的）
        $webIndex = [
            16 => 'trendsgal',
            18 => 'nastydress',
            19 => 'zaful',
            22 => 'gamiss',
            17 => 'twinkledeals',
            23 => 'stylebest',
            6 => 'dresslily',
            11 => 'rosegal',
            10 => 'rosewholesale',
            3 => 'sammydress',
            12 => 'gearbest',
            1 => 'everbuying',
            9 => 'igogo',
            2 => 'dealsmachine',
            25 => 'langria',
            21 => 'dezzal',
            26 => 'zanbase',
            27 => 'zanstyle',
            20 => 'yoshop',
            30 => 'affiliate',
            31 => 'ebay',
            99 => 'EMP',
        ];
        $this->configInfo['site'] = $webIndex; 
    }
    
    /**
     * 处理列表数据
     */
    private function dealListData($listData)
    {
        foreach ($listData as $listDataKey => $listDataVal) {
            $listData[$listDataKey] = $this->dealResultData($listDataVal);
        }
        return $listData;
    }
    
    /**
     * 
     */
    /**
     * 处理查询出来的数据
     * @param type $data
     */
    private function dealResultData($data)
    {
        $data['system_id_name'] = $this->configInfo['system_id'][$data['system_id']]; // 监控的系统名称
        $data['monitor_mode_name'] = $this->configInfo['monitor_mode'][$data['monitor_mode']]; // 监控类型
        $data['monitor_condition_type_name'] = $this->configInfo['monitor_condition_type'][$data['monitor_condition_type']]; // 监控条件类型
        $data['report_type_name'] = $this->configInfo['report_type'][$data['report_type']]; // 告警触发条件

        // 告警通知方式
        $data['report_channel_arr'] = $reportChannelArr =  explode(',',$data['report_channel']);
        $reportChannelNameArr = [];
        $reportChannelNameStr = "";
        if (!empty($reportChannelArr)) {
            foreach ($reportChannelArr as $reportChannelArrVal) {
                $reportChannelNameArr[] = $this->configInfo['report_channel'][$reportChannelArrVal];
            }
            $reportChannelNameStr = join(',',$reportChannelNameArr);
        }
        $data['report_channel_name'] = $reportChannelNameStr;

        // 告警触发条件
        $data['report_type_name'] = $this->configInfo['report_type'][$data['report_type']];

        // 配置状态
        $data['status_name'] = $this->configInfo['status'][$data['status']];
        
        // 是否需要重试
        $data['is_need_retry_name'] = $this->configInfo['is_need_retry'][$data['is_need_retry']];

        // 联系人组
        if (isset($this->configInfo['site'][$data['contacts_group_id']])) {
            $data['contacts_group_id_name'] = $this->configInfo['site'][$data['contacts_group_id']];
        } else {
            $data['contacts_group_id_name'] = "";
        }

        // 时间处理
        if ($data['monitor_mode'] == 0) { // 周期性监控
            $curDate = \yii::$app->bjTime->date('Y-m-d');
            $curDayStart = \yii::$app->bjTime->strtotime($curDate . " 00:00:00");
            $data['monitor_time_name'] = \yii::$app->bjTime->date('H:i',($curDayStart + $data['monitor_time']));
        } elseif ($data['monitor_mode'] == 1) {
            $data['monitor_time_name'] = \yii::$app->bjTime->date('Y-m-d H:i',$data['monitor_time']);
        } else {
            $data['monitor_time_name'] = $data['monitor_time'];
        }

        // 最后一次扫描时间和最后一次报警时间
        $data['last_scanning_time'] = !empty($data['last_scanning_time']) ? \yii::$app->bjTime->date('Y-m-d H:i:s',$data['last_scanning_time']) : '--';
        $data['last_report_time'] = !empty($data['last_report_time']) ? \yii::$app->bjTime->date('Y-m-d H:i:s',$data['last_report_time']) : '--';
        return $data;    
    }
    
    /**
     * 单条数据处理
     */
    private function dealOneData($oneData)
    {
        return $this->dealResultData($oneData);
    }
    
    /**
     * 监控配置列表
     */
    public function actionMonitorConfigList()
    {
        $monitorReport = new MonitorReport();
        $configList = $monitorReport->getList();
        $configList = $this->dealListData($configList);
        return $this->render('//monitor-report/monitor-config-list',[
           'configList'=>$configList,
        ]);    
    }
    
    /**
     * 改变状态信息
     */
    public function actionMonitorConfigStatus()
    {
        $mothod = strtolower(Yii::$app->request->getMethod());
        $id = Yii::$app->request->$mothod('id') ? Yii::$app->request->$mothod('id') : 0;
        $id = intval($id);
        $updateStatus = Yii::$app->request->$mothod('updateStatus') ? Yii::$app->request->$mothod('updateStatus') : 0;
        $updateStatus = intval($updateStatus);
        if(!$id) {
            $returnData['status'] = 1;
            $returnData['errorInfo'] = "没有传递id!";
            echo json_encode($returnData);
            exit;
        }
        $monitorReport = new MonitorReport();
        if($monitorReport->changeStatus($id,$updateStatus)) {
            $returnData['status'] = 0;
            $returnData['errorInfo'] = "success!";
        } else {
            $returnData['status'] = 1;
            $returnData['errorInfo'] = "数据不存在或更新失败!";
        }
        echo json_encode($returnData);
        exit;
    }
    
    /**
     * 删除某一监控项
     */
    public function actionMonitorConfigDel()
    {
        $mothod = strtolower(Yii::$app->request->getMethod());
        $id = Yii::$app->request->$mothod('id') ? Yii::$app->request->$mothod('id') : 0;
        $delId = intval($id);
        if(!$delId) {
            $returnData['status'] = 1;
            $returnData['errorInfo'] = "没有传递id!";
            echo json_encode($returnData);
            exit;
        }
        $monitorReport = new MonitorReport();
        if($monitorReport->del($delId)) {
            $returnData['status'] = 0;
            $returnData['errorInfo'] = "success!";
        } else {
            $returnData['status'] = 1;
            $returnData['errorInfo'] = "数据不存在或删除失败!";
        }
        echo json_encode($returnData);
        exit;
    }
    
    /**
     * 编辑配置相关
     */
    public function actionMonitorConfigEdit()
    {
        $mothod = strtolower(Yii::$app->request->getMethod());
        $formData = Yii::$app->request->$mothod();
        // 网站信息 （先做成和营销邮件的一致）
        $siteInfo = $this->configInfo['site'];
        
        if (empty($formData['id'])) {
            exit('你好，没有传递相关配置的id值');
        } else {
            $formData['id'] = intval($formData['id']);
        } 
        $monitorReport = new MonitorReport();
        $monitorConfigInfo = $monitorReport->getOneById($formData['id']);
        
        if (empty($monitorConfigInfo)) {
           exit('你好，当前信息不存在或已被删除'); 
        }
        $monitorConfigInfo = $this->dealOneData($monitorConfigInfo);
       
        return $this->render('//monitor-report/edit',[
            'monitorConfigInfo'=>$monitorConfigInfo,
            'siteInfo'=>$siteInfo,
        ]);    
    }
    
    /**
     * 新增配置相关
     */
    public function actionMonitorConfigAdd()
    {
        // 网站信息 （先做成和营销邮件的一致）
        $siteInfo = $this->configInfo['site'];
        return $this->render('//monitor-report/add',[
            'siteId'=>99,
            'siteInfo'=>$siteInfo,
        ]);    
    }
    
    /**
     * 数据筛选处理
     */
    private function submitFilter($formData)
    {
        $returnData = [];
        
        // 监控时间
        if (empty($formData['monitor_time'])) {
            $returnData['status'] = 1;
            $returnData['errorInfo'] = "没有设置监控时间!"; 
            return $returnData;
        }
        
        // 监控条件
        if (empty($formData['monitor_condition'])) {
            $returnData['status'] = 1;
            $returnData['errorInfo'] = "没有设置监控条件!"; 
            return $returnData;
        }
        
        // 告警渠道
        if (empty($formData['report_channel'])) {
            $returnData['status'] = 1;
            $returnData['errorInfo'] = "没有设置告警渠道!"; 
            return $returnData;
        }
        
        // 告警描述信息
        if (empty($formData['report_description'])) {
            $returnData['status'] = 1;
            $returnData['errorInfo'] = "没有设置告警描述信息!"; 
            return $returnData;
        }
        
        // 联系人分组
        if (empty($formData['contacts_group_id'])) {
            $returnData['status'] = 1;
            $returnData['errorInfo'] = "没有选择联系人分组!"; 
            return $returnData;
        }
        
        // 判断sql中是否含有更新删除信息
        if (empty($formData['monitor_condition_type'])) {
            if (strpos($formData['monitor_condition'],'delete') === true || strpos($formData['monitor_condition'],'drop') === true 
                    || strpos($formData['monitor_condition'],'truncate') === true || strpos($formData['monitor_condition'],'update') === true 
                    || strpos($formData['monitor_condition'],'create') === true || strpos($formData['monitor_condition'],'replace') === true) {
                $returnData['status'] = 1;
                $returnData['errorInfo'] = "sql中只能包含查询语句!"; 
                return $returnData;
            }
        }
        
        // 如果是每隔指定分钟监控则填写的时间不能小于5分钟
        if ($formData['monitor_mode'] == 2 && $formData['monitor_time'] < 5) {
            $returnData['status'] = 1;
            $returnData['errorInfo'] = "监控设置时间间隔不能太小!"; 
            return $returnData;
        }
        $returnData['status'] = 0;
        return $returnData;
    }
    
    /**
     * 表单数据处理
     */
    public function dealData($formData)
    {
        // 时间转换
        if ($formData['monitor_mode'] == 0) { // 周期性监控
            $curDate = \yii::$app->bjTime->date('Y-m-d');
            $curDayStart = \yii::$app->bjTime->strtotime($curDate . " 00:00:00");
            $monitorTime = yii::$app->bjTime->strtotime($curDate . $formData['monitor_time']. ":00") - $curDayStart;
            $formData['monitor_time'] = $monitorTime;
        } elseif($formData['monitor_mode'] == 1) {
            $monitorTime = \yii::$app->bjTime->strtotime($formData['monitor_time']);
            $formData['monitor_time'] = $monitorTime;
        }
        
        // 告警渠道转换
        $formData['report_channel'] = join(',' , $formData['report_channel']);
        return $formData;
    }
    
    /**
     * 编辑配置提交相关
     */
    public function actionMonitorConfigEditSubmit()
    {
        $mothod = strtolower(Yii::$app->request->getMethod());
        $formData = Yii::$app->request->$mothod();
        if (empty($formData['id'])) {
            $returnData['status'] = 1;
            $returnData['errorInfo'] = "没有传递id值";
            echo json_encode($returnData);
            exit;
        }
        try {
            $returnData = $this->submitFilter($formData);
            if ($returnData['status'] == 1) {
                echo json_encode($returnData);
                exit;
            } else {
                
                // 数据处理
                $formData = $this->dealData($formData);
               
                // 数据处理入库
                $monitorReport = new MonitorReport();
                $id = intval($formData['id']);
                unset($formData['id']);
                $returnFlag = $monitorReport->updateById($formData,$id);
                if ($returnFlag) {
                    $returnData['status'] = 0;
                    $returnData['errorInfo'] = "success";
                } else {
                    $returnData['status'] = 1;
                    $returnData['errorInfo'] = "数据更新失败";
                }
            }   
        } catch (\Exception $ex) {
            $returnData['status'] = 1;
            $returnData['errorInfo'] = $ex->getMessage();
        }
        echo json_encode($returnData);
    }
    
    /**
     * 新增配置提交相关
     */
    public function actionMonitorConfigAddSubmit()
    {
        $mothod = strtolower(Yii::$app->request->getMethod());
        $formData = Yii::$app->request->$mothod();
        try {
            $returnData = $this->submitFilter($formData);
            if ($returnData['status'] == 1) {
                echo json_encode($returnData);
                exit;
            } else {
                
                // 数据处理
                $formData = $this->dealData($formData);
               
                // 数据处理入库
                $monitorReport = new MonitorReport();
               
                $returnFlag = $monitorReport->add($formData);
                if ($returnFlag) {
                    $returnData['status'] = 0;
                    $returnData['errorInfo'] = "success";
                } else {
                    $returnData['status'] = 1;
                    $returnData['errorInfo'] = "数据插入失败";
                }
            }   
        } catch (\Exception $ex) {
            $returnData['status'] = 1;
            $returnData['errorInfo'] = $ex->getMessage();
        }
        echo json_encode($returnData);
    }
    
    /**
     * 过滤掉当次不需要监控的数据
     */
    private function filterScanningMonitorConfig($configList)
    {
        $curTime = time();
        $curDate = \yii::$app->bjTime->date('Y-m-d');
        $curDayStart = \yii::$app->bjTime->strtotime($curDate . " 00:00:00");
        foreach ($configList as $key=>$configListVal) {
            
            /*****************过滤数据开始******************/
            // 最后一次更新时间小于最后一次告警时间并且最后一次告警信息状态为1（说明监控告警配置项设置有问题）过滤
            if ($configListVal['last_report_status'] == 1 && $configListVal['last_report_time'] > $configListVal['update_time']) {
                unset($configList[$key]);
            }
            
            // 如果是一次性监控并且(当前时间没有达到监控设置时间点或者已经有告警时间并且告警时间大于监控时间设置点)(说明没有到达监控时间点或者已经监控告警完毕) 过滤
            if ($configListVal['monitor_mode'] == 1 && ($curTime < $configListVal['monitor_time'] || $configListVal['last_report_time'] > $configListVal['monitor_time'])) {
                unset($configList[$key]);
            }
            
            // 如果是按天周期性监控并且(当前时间没有达到当天设置的监控时间点或者最后一次告警时间大于当天的监控设置时间点) 过滤
            if ($configListVal['monitor_mode'] == 0) {
                $curDayMonitorTime = $curDayStart + $configListVal['monitor_time'];
                if ($curTime < $curDayMonitorTime || $configListVal['last_report_time'] >= $curDayMonitorTime) {
                    unset($configList[$key]);
                }
            }
            
            // 如果是每个指定分钟监控并且当前时间-最后一次告警时间点 < 设置的间隔时间点 过滤
            if ($configListVal['monitor_mode'] == 2 && ($curTime-$configListVal['last_report_time']) < $configListVal['monitor_time']*60) {
                unset($configList[$key]);
            }
            /*****************过滤数据结束******************/
        }
        return $configList;
    }
    
    /**
     * 计划任务扫描监控信息，符合告警条件则触发告警
     */
    public function actionScanningMonitorConfig()
    {
        ignore_user_abort(true);     // 忽略用户的中断
        set_time_limit(0);
        
        // 添加redis锁，保证只有一个进程在运行
        $redis = new RedisMarketingEmail();
        $redisLock = new RedisLock($redis,__FUNCTION__, 1200);
        if ($redisLock->translock() === false) {
            $msg = "当前已经有一个监控告警扫描的进程在跑";
            Elog::logCrontab($msg,__FUNCTION__,'info');
            return 0;
        }
        
        // 查询所有未删除并且启用的配置信息
        $monitorReport = new MonitorReport();
        $configList = $monitorReport->getList(["status"=>1]);
        
        if ($configList) {
            // 过滤不需要告警的数据
            $configList = $this->filterScanningMonitorConfig($configList);
        }
       
        
        // 启动具体每项监控进程
        if ($configList) {
            $toolkit = new Toolkit();
            $curl = $toolkit->getCurl();
            $curl->onInfo = null;
            foreach ($configList as $configListVal) {
                $curl->add(
                        array(
                            'opt' => array(
                                CURLOPT_URL => "http://{$_SERVER['HTTP_HOST']}/monitor-report/report-one?id={$configListVal['id']}",
                            )
                        ),
                        function ($r) {
                            echo "Request success for " . $r['info']['url'] . "\n";
                            echo $r['body']."\n";
                        }); 
            }
            $curl->start();
        }
        $redisLock->unlock();
    }

    /**
     * 对某一配置项进行告警
     */
    public function actionReportOne()
    {
        ignore_user_abort(true);     // 忽略用户的中断
        set_time_limit(0);
        $monitorReportComponent = new MonitorReportComponent();
        $mothod = strtolower(Yii::$app->request->getMethod());
        $formData = Yii::$app->request->$mothod();
        if (empty($formData['id'])) {
            exit('必须传递配置项id');
        } 
        $id = intval($formData['id']);
        
        // 添加redis锁，保证某个报警配置项只有一个进程在运行
        $redis = new RedisMarketingEmail();
        $redisLock = new RedisLock($redis,__FUNCTION__ . "_{$id}_report", 1200);
        if ($redisLock->translock() === false) {
            $msg = "id为{$id}配置项已经有一进程在跑";
            Elog::logCrontab($msg,__FUNCTION__,'info');
            return 0;
        }
        
        // 获取当前配置项信息
        $monitorReport = new MonitorReport();
        $monitorConfigInfo = $monitorReport->getOneById($id);
        if (empty($monitorConfigInfo)) {
            exit('改配置项已经不存在');
        }
        $reportInfo = [];
        if ($monitorConfigInfo['monitor_condition_type'] == 0) { // 说明是sql
            try {
                // 变量替换
                $monitorConfigInfo['monitor_condition'] = $this->sqlVariableReplace($monitorConfigInfo['monitor_condition']);
                $selectResult = $monitorReport->executeSql($monitorConfigInfo['monitor_condition'],$monitorConfigInfo['system_id']);  
                /************** 判断数据是否正常，正常则只写入数据库，不正常则写入数据库并且需要告警 start *******/
                $reportInfo = $monitorReportComponent->judgeIsNeedReport($selectResult,$monitorConfigInfo);
                $reportInfo['report_message'] = $monitorReportComponent->reportMessageVariableReplace($monitorConfigInfo['report_description'],$selectResult);
                /************** 判断数据是否正常，正常则只写入数据库，不正常则写入数据库并且需要告警 end  *******/
            } catch (\Exception $ex) {
                // 自身监控条件异常
                $reportInfo['monitor_id'] = $monitorConfigInfo['id'];
                $reportInfo['is_need_report'] = 1;
                $reportInfo['is_self'] = 1;
                $reportInfo['report_message'] = "id为{$reportInfo['monitor_id']}的监控自身配置有异常，具体异常信息为:" . $ex->getMessage();
            }
        } elseif ($monitorConfigInfo['monitor_condition_type'] == 1) {
            try {
                $monitorConditionReturnInfo = [];
                $toolkit = new Toolkit();
                $curl = $toolkit->getCurl();
                $curl->onInfo = null;
                if (strpos($monitorConfigInfo['monitor_condition'],"http://") !== false) {
                    $monitorConditionUrl = trim($monitorConfigInfo['monitor_condition']);
                } else {
                    if (YII_ENV == "product") {
                        $monitorConditionUrl = "http://10.61.232.254:86" . trim($monitorConfigInfo['monitor_condition']);
                    } else {
                        $monitorConditionUrl = "http://{$_SERVER['HTTP_HOST']}" . trim($monitorConfigInfo['monitor_condition']);
                    }
                }
                $curl->add(
                        array(
                            'opt' => array(
                                CURLOPT_URL => $monitorConditionUrl,
                            ),
                            'args' => $monitorConfigInfo,
                        ),
                        [$monitorReportComponent,'monitorConditionSuccess']
                ); 
                $curl->start(); 
                // 如果url模式 自身没有异常则不需要再当前方法中进行监控告警而是在monitorConditionSuccess方法中进行相关操作
                $isNotCurReport = 1;
            } catch (\Exception $ex) {
                // 自身监控条件异常
                $reportInfo['monitor_id'] = $monitorConfigInfo['id'];
                $reportInfo['is_need_report'] = 1;
                $reportInfo['is_self'] = 1;
                $reportInfo['report_message'] = "id为{$reportInfo['monitor_id']}的监控自身配置有异常，具体异常信息为:" . $ex->getMessage();
            }
        }
        if (empty($isNotCurReport)) {
            
            // 数据写入monitor_report_message表
            $monitorReportComponent->addReportAndReport($reportInfo,$monitorConfigInfo);
        }
        $redisLock->unlock();
        if (!empty($formData['isAjax'])) {
            $returnDataReport['status'] = 0;
            $returnDataReport['errorInfo'] = "success!";
            echo json_encode($returnDataReport);
            exit;
        }
    }

    /**
     * sql语句相关变量替换
     * @param type $sql
     */
    private function  sqlVariableReplace($sql) {
        $replaceArr=["{curtime}"];
        $toArr=[];
        $toArr[] = time();
        return str_replace($replaceArr, $toArr, $sql);
    }
}
